package com.poetry.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.poetry.constant.FormatConstant;
import com.poetry.constant.LoginConstant;
import com.poetry.constant.RedisConstant;
import com.poetry.domain.dto.LoginDTO;
import com.poetry.domain.dto.UserDTO;
import com.poetry.domain.po.User;
import com.poetry.exception.R;
import com.poetry.exception.UnauthorizedException;
import com.poetry.mapper.UserMapper;
import com.poetry.service.IUserService;
import com.poetry.util.AliOssUtil;
import com.poetry.util.EmailUtil;
import com.poetry.util.UserContext;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements IUserService {
    @Resource
    private EmailUtil emailUtil;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private AliOssUtil aliOssUtil;

    @Override
    public R<String> login(LoginDTO loginDTO) {
        if (!emailUtil.checkEmail(loginDTO.getEmail())) {
            return R.error("邮箱格式错误", 404);
        }
        User user = query().eq("email", loginDTO.getEmail()).one();
        if (LoginConstant.EMAIL_LOGIN.equals(loginDTO.getMethod())) {
            if (!emailUtil.verifyCode(loginDTO.getEmail(), loginDTO.getCode())) {
                return R.error("无效验证码", 404);
            }
        }
        if (LoginConstant.PASSWORD_LOGIN.equals(loginDTO.getMethod())) {
            String password = loginDTO.getPassword();
            if (password.length() < 8 || password.length() > 16) {
                return R.error("密码长度8~16位", 404);
            }
            if (!Pattern.compile(FormatConstant.REGEX).matcher(password).matches()) {
                return R.error("密码需要同时包含数字、字母和特殊符号中的至少两种类型", 404);
            }
            password = DigestUtils.md5DigestAsHex((LoginConstant.SALT + loginDTO.getPassword()).getBytes());
            if (user != null && !user.getPassword().equals(password)) {
                return R.error("密码错误", 404);
            }
        }
        if (user == null) {
            user = new User();
            BeanUtils.copyProperties(loginDTO, user);
            this.save(user);
        }
        String token = UUID.randomUUID().toString();
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        userDTO.setIcon("111");
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor(((fieldName, fieldValue) -> fieldValue.toString())));
        String tokenKey = RedisConstant.LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
        stringRedisTemplate.expire(tokenKey, RedisConstant.LOGIN_USER_TTL, TimeUnit.MINUTES);
        return R.success("登录成功", token);
    }

    @Override
    public R<String> sendVerificationCode(LoginDTO loginDTO) {
        if (!emailUtil.checkEmail(loginDTO.getEmail())) {
            return R.error("邮箱格式错误");
        }
        emailUtil.saveCode(loginDTO.getEmail());
        try {
            emailUtil.sendVerificationCode(loginDTO.getEmail());
        } catch (Exception e) {
            throw new UnauthorizedException("邮箱发送失败");
        }
        return R.success(null, "验证码发送成功");
    }

    @Override
    @Transactional
    public R<String> resetPassword(LoginDTO loginDTO) {
        User user = this.getById(UserContext.getUserId());
        String password = loginDTO.getPassword();
        if (!emailUtil.checkEmail(loginDTO.getEmail())) {
            return R.error("邮箱格式错误", 404);
        }
        if (password.length() < 8 || password.length() > 16) {
            return R.error("密码长度8~16位", 404);
        }
        if (!Pattern.compile(FormatConstant.REGEX).matcher(password).matches()) {
            return R.error("密码需要同时包含数字、字母和特殊符号中的至少两种类型", 404);
        }
        if (!emailUtil.verifyCode(loginDTO.getEmail(), loginDTO.getCode())) {
            return R.error("无效验证码", 404);
        }
        if (!user.getEmail().equals(loginDTO.getEmail())) {
            return R.error("邮箱验证失败");
        }
        password = DigestUtils.md5DigestAsHex((LoginConstant.SALT + loginDTO.getPassword()).getBytes());
        user.setPassword(password);
        boolean isSuccess = this.updateById(user);
        if (!isSuccess) {
            return R.error("重置密码失败");
        }
        return R.success(null, "密码重置成功");
    }

    @Transactional
    @Override
    public R<User> updateMessage(UserDTO userDTO) {
        User user = this.getById(UserContext.getUserId());
        String newName = userDTO.getUsername();
        if (newName.isEmpty() || newName.length() > 12) {
            return R.error("用户名称长度只能在1-12之间");
        }
        user.setUsername(newName);
        user.setIcon(userDTO.getIcon());
        boolean isSuccess = this.updateById(user);
        if (!isSuccess) {
            return R.error("设置失败");
        }
        return R.success("用户名称设置成功", user);
    }

    @Override
    public R<String> uploadFile(MultipartFile file) {
        try {
            return R.success("文件上传成功", aliOssUtil.uploadFile(file));
        } catch (Exception e) {
            throw new UnauthorizedException("文件上传失败");
        }
    }

    @Override
    public R<String> sign() {
        // 1.获取当前登录用户
        Integer userId = UserContext.getUserId();
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3.拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstant.USER_SIGN_KEY + userId + keySuffix;
        // 4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        // 5.写入Redis SETBIT key offset 1
        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
        return R.success(null, "签到成功");
    }

    @Override
    public R<Integer> signCount() {
        // 1.获取当前登录用户
        Integer userId = UserContext.getUserId();
        if (userId == null) {
            return R.error("用户未登录");
        }
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3.拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = RedisConstant.USER_SIGN_KEY + userId + keySuffix;
        // 4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        // 5.获取本月截止今天为止的所有的签到记录，返回的是一个十进制的数字 BITFIELD sign:5:202203 GET u14 0
        List<Object> result = stringRedisTemplate.executePipelined(
                (RedisCallback<Boolean>) connection -> {
                    for (int i = 0; i < dayOfMonth; i++) {
                        connection.getBit(key.getBytes(), i);
                    }
                    return null;
                }
        );
        // 6.循环遍历
        int count = 0;
        for (int i = dayOfMonth - 1; i >= 0; i--) {
            Boolean signed = (Boolean) result.get(i);
            if (signed != null && signed) {
                count++;
            } else {
                break; // 遇到未签到，终止循环
            }
        }
        return R.success("获取签到结果成功", count);
    }
}
